﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Data;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace BestPrice.Core
{
    using Common;

    public class BestPriceDataContext : DbContext
    {
        public BestPriceDataContext()
            : this(CoreSetting.NameCnString)
        {   
        }

        public BestPriceDataContext(String nameOrCnString)
            : base(nameOrCnString)
        {
        }

        private static BestPriceDataContext _DbContext;
        public static BestPriceDataContext GetDbContext()
        {
            if (_DbContext == null)
                _DbContext = new BestPriceDataContext();

            return _DbContext;
        }

        public DbSet GetDbSet<E>()
            where E : BaseEntity
        {
            var context = this;
            return context.Set<E>();
        }

        #region Required Function
        public DbSet GetDbSet(Type type)
        {
            return Set(type);
        }

        public E Get<E>(E e)
            where E : BaseEntity
        {
            throw new InvalidCastException();
        }

        public E GetByID<E, ID>(ID id)
            where E : BaseEntity
            where ID : BaseKey
        {
            var keys = id.Members;

            if (keys.Length >= 1)
            {
                var dbSet = this.Set<E>();
                var type = typeof(E);
                var param = Expression.Parameter(type, type.Name);

                Expression expKey = null;
                Expression expValue = null;

                BinaryExpression binaryExpression = null;
                foreach (var key in keys)
                {
                    expKey = Expression.Property(param, key.Key);
                    expValue = Expression.Constant(key.Value);

                    if (binaryExpression == null)
                        binaryExpression = Expression.Equal(expKey, expValue);
                    else
                        binaryExpression = Expression.And(binaryExpression, Expression.Equal(expKey, expValue));
                }

                var predicate = Expression.Lambda<Func<E, bool>>(binaryExpression, new ParameterExpression[] { param });
                return dbSet.SingleOrDefault(predicate);
            }

            throw new InvalidCastException();
        }

        public bool Insert<E>(E e)
            where E : BaseEntity
        {
            Entry<E>(e).State = EntityState.Added;
            return SaveChanges() > 0;
        }

        public bool Update<E>(E e)
            where E : BaseEntity
        {
            Entry<E>(e).State = EntityState.Modified;
            return SaveChanges() > 0;
        }

        public bool Delete<E>(E e)
            where E : BaseEntity
        {
            Entry<E>(e).State = EntityState.Deleted;
            return SaveChanges() > 0;
        }

        public E DeleteByID<E, ID>(ID id)
            where E : BaseEntity
            where ID : BaseKey
        {
            var e = GetByID<E, ID>(id);
            Entry<E>(e).State = EntityState.Deleted;
            SaveChanges();
            return e;
        }
        #endregion

        #region Model Creating
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Entity<Category>().HasKey(e => new { e.ID });
            modelBuilder.Entity<WebSite>().HasKey(e => new { e.ID });
            modelBuilder.Entity<Formatter>().HasKey(e => new { e.WebSiteID });
            modelBuilder.Entity<WebSiteCategory>().HasKey(e => new { e.CategoryID, e.WebSiteID });

            modelBuilder.Entity<Category>().Ignore(c => c.WebSites);
            modelBuilder.Entity<WebSite>().Ignore(c => c.Formatter);

        }
        #endregion

        #region Database Set
        public DbSet<Category> Categories { get; set; }
        public DbSet<WebSite> WebSites { get; set; }
        public DbSet<Formatter> Formatters { get; set; }
        public DbSet<WebSiteCategory> WebSiteCategories { get; set; }
        #endregion
    }
}
